chore(deps): upgrade gitsheets to 2.x (Rust core, #150)#151
Merged
Conversation
Version-bump-only scope: 2.x is a Rust-core engine rewrite behind an unchanged Node API. Real work is the hologit-drop blob-write migration + two documented byte re-baselines (integer underscores, markdown bodies). The three cache workarounds are ported, not deleted — gitsheets#184 (the per-sheet refresh API that would let them go) is open and not in 2.x. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Upgrade gitsheets ^1.4.1 → ^2.2.0. The 2.x line rewrites the core in Rust (via @gitsheets/core-napi) but keeps the Node public API surface (openRepo/openStore/Sheet/Transaction) unchanged. Command run: npm install gitsheets@^2.2.0 -w apps/api hologit is dropped as a transitive dependency in 2.x; it no longer appears in the lockfile. Code migration (avatar blob-write path) is in the next commit. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
gitsheets 2.x drops hologit as a dependency. Replace the
BlobObject.write(hologitRepo, buf) pattern with the 2.x surface:
const blob = await repo.writeBlob(buffer: Buffer) // → BlobHandle
await sheet.setAttachments(record, { 'name.jpg': blob })
Sites migrated:
- apps/api/src/routes/people.ts: avatar upload (original + 128 thumb)
- apps/api/scripts/import-laddr/importer.ts: legacy avatar + blog-media
The `as unknown as string` casts are removed — 2.x writeBlob takes
a Buffer natively, matching the actual runtime types throughout.
The three workarounds for gitsheets#184 (swapPublic in store.ts,
git cat-file in attachments.ts, data-repo-lock.ts) are unaffected
and confirmed to still compile. They are kept as-is pending upstream
resolution.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
gitsheets 2.x (Rust core) refuses to marshal a null- or undefined- valued field to TOML — `serializeRecords`/`upsert` throw "cannot marshal JS value of type Null/Undefined to a TOML value". gitsheets 1.4.1 (`@iarna/toml`) silently dropped such keys, so they were never written to disk (verified against the on-disk `published` snapshot — no record carries a null-valued key). Our Zod schemas mark optional fields `.nullable().optional()` and the write services normalize cleared fields to `?? null`, so under 2.x every write of a record with a cleared optional field threw a 500. This surfaced as 16 test failures across write-api, people-lifecycle, and import-laddr (all "cannot marshal ... Undefined/Null"). Fix: wrap the per-sheet Standard Schema validator in openPublicStore so the validated record has null/undefined-valued keys stripped (recursively) before it reaches the core marshaller. gitsheets runs the validator host-side and marshals its output, so this is the single authoritative write boundary. The result is byte-identical to 1.4.1's on-disk form: an absent optional field is an absent TOML key. This is not one of the two documented 2.x re-baselines (integer underscores, markdown bodies) — it's an undocumented marshal-contract change in the Rust core that the upgrade plan did not anticipate. Adds a focused store test asserting null-valued keys are dropped and present fields survive (also pins the integer-underscore re-baseline). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tion Record the undocumented null/undefined marshal-contract change found during the bump (Rust core throws where 1.4.1 dropped the key) and the stripNullish fix at the write boundary. Check off the validation checklist; note no test needed a re-baseline update. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The `not.toContain('null')` check tripped on the fixture's own
"Nullish Person" / "nullish-person" strings. The three field-absence
regexes above already verify null-valued keys aren't written; replace
the broad substring check with `not.toMatch(/=\s*null\b/)` — no field is
assigned a bare null value.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Upgrades
gitsheets^1.4.1→^2.2.0(the Rust-core line). Closes the version-bump half of #150; the "retire the 3 cache workarounds" half stays open (blocked on upstream gitsheets#184, which is not in 2.x).Scope
Version bump only. The three #184 workarounds (
swapPublic, attachmentsgit cat-file,data-repo-lock) are kept/verified, not deleted — #184 (per-sheet refresh API) is open and not shipped in 2.x (2.x'sSheetstill snapshots the pre-commit tree).What actually broke (2.x is a "no public-API change" engine rewrite, so this is small)
hologitdropped as a gitsheets dep → migrated the avatar/media blob-write path offhologit'sBlobObject.writeto 2.xrepo.writeBlob(buf): Promise<BlobHandle>+setAttachments. Sites:routes/people.ts(avatar upload),import-laddr/importer.ts(legacy avatar + blog media).as unknown as stringcasts removed (2.x takes aBuffer).null/undefined-valued field; 1.4.1 silently dropped such keys. Our Zod schemas use.nullable().optional()and write services normalize cleared fields to?? null. Fix: astripNullishat the single validator write-boundary instore/public.tsdrops null/undefined keys before marshalling — keeping the on-disk form byte-identical to 1.4.1 (an absent optional field is simply an absent TOML key). Covered by a newstore.test.tscase.Byte re-baseline (deliberate, lossless)
legacyId = 31_618→31618(values unchanged; confirmed via the Rust core directly). Matches gitsheets#196.blog-posts.Validation
not.toContain('null')that false-matched the fixture's own "Nullish Person" name; the field-absence checks it duplicated already cover the real contract).@gitsheets/core-napi) loads and round-trips on darwin/arm64.Follow-ups (in
plans/gitsheets-2x-bump.md)codeforphilly-data(one deliberate commit to re-serialize all records to the new canonical form) — a data-ops task, bundled with cutover data prep. Until then records re-normalize as they're written.🤖 Generated with Claude Code